package com.vcc.boss.core.queue.transaction.queue;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.vcc.boss.core.queue.transaction.model.RiskQueueDto;
import com.vcc.channel.base.model.request.CardCancelDto;
import com.vcc.channel.base.service.ChannelBaseService;
import com.vcc.core.constant.CardStatusEnum;
import com.vcc.data.entity.Risk;
import com.vcc.data.entity.RiskRule;
import com.vcc.data.entity.RiskTransactionLog;
import com.vcc.data.entity.Transaction;
import com.vcc.service.service.*;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executors;


@Log4j2
@Component
public class RiskQueue implements CommandLineRunner {

    @Resource
    private TransactionService transactionService;
    @Resource
    private RiskService riskService;
    @Resource
    private RiskTransactionLogService riskTransactionLogService;
    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private CardOrderService cardOrderService;

    private final DelayQueue<RiskQueueDto> delayQueue = new DelayQueue<RiskQueueDto>();
    @Resource
    private UserService userService;
    @Resource
    private UserCardService userCardService;
    @Resource
    private RiskRuleService riskRuleService;

    private static List<Risk> risks = new ArrayList<>();

    private static List<RiskRule> riskRules = new ArrayList<>();

    public void init() {
        log.info("规则初始化");
        risks = riskService.list();
        riskRules = riskRuleService.list();
        log.info("规则初始化完成,规则数量:{}", risks.size());
    }

    public void refresh() {
        log.info("规则刷新");
        risks = riskService.list();
        riskRules = riskRuleService.list();
        log.info("规则刷新完成,规则数量:{}", risks.size());
    }


    public void add(String channelTransactionId) {
        //延迟 10 秒
        synchronized (this) {
            if (risks.isEmpty()) {
                init();
            }
            RiskQueueDto riskQueueDto = delayQueue.stream().filter(it -> it.getChannelTransactionId().equals(channelTransactionId)).findFirst().orElse(null);
            if (riskQueueDto == null) {
                DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), 5);
                log.info("当前添加的是{},秒${}", channelTransactionId, 5);
                delayQueue.put(
                        new RiskQueueDto(
                                channelTransactionId, dateTime.getTime()
                        )
                );
            }
        }
    }


    @Override
    public void run(String... args) throws Exception {
        Executors.newSingleThreadExecutor().execute(new Thread(this::excuteThread));
    }

    private void excuteThread() {
        while (true) {
            try {
                processTask(delayQueue.take());
            } catch (InterruptedException e) {
                break;
            }
        }
    }


    private void processTask(RiskQueueDto riskQueueDto) {
        log.info("当前执行的是:{},时间是:{}", riskQueueDto.getChannelTransactionId(), DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        //TODO 这里处理具体的业务逻辑
        Transaction transaction = transactionService.getByChannelTranId(riskQueueDto.getChannelTransactionId());
        if (transaction == null) {
            log.error("当前交易不存在,交易号:{}", riskQueueDto.getChannelTransactionId());
            return;
        }
        log.info("当前交易需要检查风控,交易号:{}", riskQueueDto.getChannelTransactionId());
        List<Risk> baseRisk = risks.stream().filter(it -> it.getCode().equals("TRANSACTION") && it.getStatus() == 0 && it.getTargetId() == 0L).toList();
        List<Risk> cardRisk = risks.stream().filter(it -> it.getCode().equals("TRANSACTION") && it.getStatus() == 0 && it.getTargetId().equals(transaction.getCardTypeId())).toList();
        List<Risk> userRisk = risks.stream().filter(it -> it.getCode().equals("TRANSACTION") && it.getStatus() == 0 && it.getTargetId().equals(transaction.getUserId())).toList();
        log.info("当前交易需要风控,交易号:{},公共规则数量:{},用户规则数量:{},卡类型规则数量:{}", riskQueueDto.getChannelTransactionId(), baseRisk.size(), userRisk.size(), cardRisk.size());
        // 优先级 公共 > 用户 > 卡类型
        for (Risk risk : baseRisk) {
            if (checkRisk(risk, transaction)) {
                log.info("当前交易触发公共风控,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                RiskTransactionLog riskTransactionLog = RiskTransactionLog.builder()
                        .riskId(risk.getId())
                        .transactionId(transaction.getId())
                        .channelTransactionId(transaction.getChannelTranId())
                        .triggerEventDate(new Date())
                        .status(2)
                        .disposeStatus(1)
                        .disposeObject(risk.getDisposeObject())
                        .disposeFunctionName(risk.getFunctionName())
                        .hasAuto(risk.getHasAuto())
                        .remark(transaction.getRemark())
                        .amount(transaction.getMerchantAmount())
                        .currency(transaction.getMerchantCurrency())
                        .country(transaction.getMerchantCountry())
                        .mcc(transaction.getMcc())
                        .balance(getCardBalance(transaction.getChannel(), transaction.getChannelCardId()))
                        .merchantName(transaction.getMerchantName())
                        .userId(transaction.getUserId())
                        .cardTypeId(transaction.getCardTypeId())
                        .userCardId(transaction.getUserCardId())
                        .cardNumber(transaction.getCardNumber())
                        .channelName(transaction.getChannel())
                        .build();
                riskTransactionLogService.save(riskTransactionLog);
                // 触发风控
                if (risk.getHasAuto()) {
                    try {
                        boolean disposed = disposeCard(
                                transaction.getUserCardId(),
                                transaction.getCardTypeId(),
                                transaction.getChannelCardId(),
                                transaction.getUserId(),
                                transaction.getCardNumber(),
                                transaction.getChannel(),
                                risk.getFunctionName()
                        );
                        if (disposed) {
                            // 如果公共风控已经处理了，则不再处理后续的风控
                            log.info("当前交易公共风控已处理,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                            riskTransactionLogService.completeTransactionLog(
                                    riskTransactionLog.getId(),
                                    0,
                                    "自动处理成功"
                            );

                        }
                    } catch (Exception e) {
                        riskTransactionLogService.completeTransactionLog(
                                riskTransactionLog.getId(),
                                1,
                                "自动处理失败" + e.getMessage()
                        );
                    }
                } else {
                    log.info("当前交易未触发自动公共风控,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                    riskTransactionLogService.completeTransactionLog(
                            riskTransactionLog.getId(),
                            2,
                            "触发风控，需要手动处理"
                    );
                }
                return;
            }
        }
        for (Risk risk : userRisk) {
            if (checkRisk(risk, transaction)) {
                log.info("当前交易触发用户风控,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                // 触发风控
                RiskTransactionLog riskTransactionLog = RiskTransactionLog.builder()
                        .riskId(risk.getId())
                        .transactionId(transaction.getId())
                        .channelTransactionId(transaction.getChannelTranId())
                        .triggerEventDate(new Date())
                        .status(2)
                        .disposeStatus(1)
                        .disposeObject(risk.getDisposeObject())
                        .disposeFunctionName(risk.getFunctionName())
                        .hasAuto(risk.getHasAuto())
                        .remark(transaction.getRemark())
                        .amount(transaction.getMerchantAmount())
                        .currency(transaction.getMerchantCurrency())
                        .country(transaction.getMerchantCountry())
                        .mcc(transaction.getMcc())
                        .balance(getCardBalance(transaction.getChannel(), transaction.getChannelCardId()))
                        .merchantName(transaction.getMerchantName())
                        .userId(transaction.getUserId())
                        .cardTypeId(transaction.getCardTypeId())
                        .userCardId(transaction.getUserCardId())
                        .cardNumber(transaction.getCardNumber())
                        .channelName(transaction.getChannel())
                        .build();
                riskTransactionLogService.save(riskTransactionLog);
                if (risk.getHasAuto()) {
                    try {
                        disposeUser(
                                transaction.getUserId(),
                                "block"
                        );
                        boolean disposed = disposeCard(
                                transaction.getUserCardId(),
                                transaction.getCardTypeId(),
                                transaction.getChannelCardId(),
                                transaction.getUserId(),
                                transaction.getCardNumber(),
                                transaction.getChannel(),
                                risk.getFunctionName()
                        );
                        if (disposed) {
                            // 如果公共风控已经处理了，则不再处理后续的风控
                            log.info("当前交易用户风控已处理,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                            riskTransactionLogService.completeTransactionLog(
                                    riskTransactionLog.getId(),
                                    0,
                                    "自动处理成功"
                            );
                            return;
                        }
                    } catch (Exception e) {
                        riskTransactionLogService.completeTransactionLog(
                                riskTransactionLog.getId(),
                                1,
                                "自动处理失败" + e.getMessage()
                        );
                    }
                } else {
                    log.info("当前交易未触发自动用户风控,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                    riskTransactionLogService.completeTransactionLog(
                            riskTransactionLog.getId(),
                            2,
                            "触发风控，需要手动处理"
                    );
                }
                return;
            }
        }
        for (Risk risk : cardRisk) {
            if (checkRisk(risk, transaction)) {
                log.info("当前交易触发卡类型风控,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                // 触发风控
                RiskTransactionLog riskTransactionLog = RiskTransactionLog.builder()
                        .riskId(risk.getId())
                        .transactionId(transaction.getId())
                        .channelTransactionId(transaction.getChannelTranId())
                        .triggerEventDate(new Date())
                        .status(2)
                        .disposeStatus(1)
                        .disposeObject(risk.getDisposeObject())
                        .disposeFunctionName(risk.getFunctionName())
                        .hasAuto(risk.getHasAuto())
                        .remark(transaction.getRemark())
                        .amount(transaction.getMerchantAmount())
                        .currency(transaction.getMerchantCurrency())
                        .country(transaction.getMerchantCountry())
                        .balance(getCardBalance(transaction.getChannel(), transaction.getChannelCardId()))
                        .mcc(transaction.getMcc())
                        .merchantName(transaction.getMerchantName())
                        .userId(transaction.getUserId())
                        .cardTypeId(transaction.getCardTypeId())
                        .userCardId(transaction.getUserCardId())
                        .cardNumber(transaction.getCardNumber())
                        .channelName(transaction.getChannel())
                        .build();
                riskTransactionLogService.save(riskTransactionLog);
                if (risk.getHasAuto()) {
                    try {
                        boolean disposed = disposeCard(
                                transaction.getUserCardId(),
                                transaction.getCardTypeId(),
                                transaction.getChannelCardId(),
                                transaction.getUserId(),
                                transaction.getCardNumber(),
                                transaction.getChannel(),
                                risk.getFunctionName()
                        );
                        if (disposed) {
                            // 如果公共风控已经处理了，则不再处理后续的风控
                            log.info("当前交易号段风控已处理,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                            riskTransactionLogService.completeTransactionLog(
                                    riskTransactionLog.getId(),
                                    0,
                                    "自动处理成功"
                            );
                        }
                    } catch (Exception e) {
                        riskTransactionLogService.completeTransactionLog(
                                riskTransactionLog.getId(),
                                1,
                                "自动处理失败" + e.getMessage()
                        );
                    }
                } else {
                    log.info("当前交易号段风控未自动处理,交易号:{},风控规则ID:{}", riskQueueDto.getChannelTransactionId(), risk.getId());
                    riskTransactionLogService.completeTransactionLog(
                            riskTransactionLog.getId(),
                            2,
                            "触发风控，需要手动处理"
                    );
                }
                return;
            }
        }


    }

    private boolean disposeCard(Long userCardId, Long cardId, String channelCardId, Long userId, String cardNumber, String channelName, String disposeFunc) {
        ChannelBaseService baseService = applicationContext.getBean(channelName, ChannelBaseService.class);
        String requestId = IdUtil.getSnowflakeNextIdStr();
        String cardStatus = baseService.queryCardStatus(channelCardId);
        if (disposeFunc.equals("block")) {
            if (cardStatus.equals(CardStatusEnum.ACTIVE.getCode())) {
                cardOrderService.blockCard(
                        requestId,
                        cardId,
                        userId,
                        0L,
                        cardNumber,
                        userCardId,
                        channelCardId,
                        "系统风控冻结卡",
                        "SYSTEM"
                );
                baseService.blockCard(channelCardId, requestId);
            }
            if (cardStatus.equals(CardStatusEnum.ACTIVE.getCode()) || cardStatus.equals(CardStatusEnum.BLOCKED.getCode())) {
                userCardService.updateCardStatusByChannelCardId(
                        CardStatusEnum.CONTROL.getCode(),
                        channelCardId
                );
            } else {
                log.info("当前卡状态不可冻结,渠道:{},卡ID:{},状态:{}", channelName, channelCardId, cardStatus);
            }
        } else {
            if (cardStatus.equals(CardStatusEnum.ACTIVE.getCode())) {
                cardOrderService.closeCard(
                        requestId,
                        cardId,
                        userId,
                        0L,
                        cardNumber,
                        userCardId,
                        channelCardId,
                        "系统风控销卡",
                        "CLIENT"
                );
                baseService.cancelCard(
                        CardCancelDto.builder()
                                .channelCardId(channelCardId)
                                .requestId(requestId)
                                .build()
                );
            }
            if (cardStatus.equals(CardStatusEnum.BLOCKED.getCode())) {
                userCardService.updateCardStatusByChannelCardId(
                        CardStatusEnum.CONTROL.getCode(),
                        channelCardId
                );
            }
        }
        return true;
    }

    private boolean disposeUser(Long userId, String disposeFunc) {
        // TODO 这里后续完善用户的风控
        if (disposeFunc.equals("block")) {
            userService.updateStatusAndAdminRemarkById(userId, 2, "用户风控冻结");
            log.info("用户风控冻结:{}", userId);
        } else {
            log.info("用户风控销户:{}", userId);
        }
        return true;
    }

    private BigDecimal getCardBalance(String channelName, String channelCardId) {
        ChannelBaseService baseService = applicationContext.getBean(channelName, ChannelBaseService.class);
        try {
            return baseService.queryCardBalance(channelCardId);
        } catch (Exception e) {
            log.error("获取卡余额失败,渠道:{},卡ID:{},错误信息:{}", channelName, channelCardId, e.getMessage());
            return BigDecimal.ZERO;
        }
    }

    private boolean checkRisk(Risk risk, Transaction transaction) {
        if (!risk.getRuleType()){
            return false;
        }
        try{
            for (RiskRule riskRule : riskRules.stream().filter(it -> it.getRiskId().equals(risk.getId())).toList()) {
                log.info("当前检查的风控ID:{},交易号:{}", risk.getId(), transaction.getChannelTranId());
                // == 等于  != 不等于  in 包含  nn 不包含
                if (riskRule.getKey().equals("mcc") && StringUtils.isNotBlank(transaction.getMcc())) {
                    log.info("风控检查MCC,风控对象:{}，风控方法：{},风控值：{},交易MCC:{}", riskRule.getKey(), riskRule.getFun(), riskRule.getValue(), transaction.getMcc());
                    return checkValue(riskRule.getFun(), riskRule.getValue(), transaction.getMcc());
                }
                if (riskRule.getKey().equals("merchant_name") && StringUtils.isNotBlank(transaction.getMerchantName())) {
                    log.info("风控检查商户名,风控对象:{}，风控方法：{},风控值：{},交易商户名:{}", riskRule.getKey(), riskRule.getFun(), riskRule.getValue(), transaction.getMerchantName());
                    return checkValue(riskRule.getFun(), riskRule.getValue(), transaction.getMerchantName());
                }
                if (riskRule.getKey().equals("country_code") && StringUtils.isNotBlank(transaction.getMerchantCountry())) {
                    log.info("风控检查国家码,风控对象:{}，风控方法：{},风控值：{},交易国家码:{}", riskRule.getKey(), riskRule.getFun(), riskRule.getValue(), transaction.getMerchantCountry());
                    return checkValue(riskRule.getFun(), riskRule.getValue(), transaction.getMerchantCountry());
                }
            }
        }catch (Exception e){
            log.error("风控检查异常,交易号:{},错误信息:{}", transaction.getChannelTranId(), e.getMessage());
            return false;
        }
        return false;
    }


    private boolean checkValue(String condition, String conditionValue, String practicalValue) {
        boolean flag = switch (condition) {
            case "==" -> conditionValue.equalsIgnoreCase(practicalValue);
            case "!=" -> !conditionValue.equalsIgnoreCase(practicalValue);
            case "in" -> practicalValue.toLowerCase().contains(conditionValue.toLowerCase());
            case "nn" -> !practicalValue.toLowerCase().contains(conditionValue.toLowerCase());
            default -> true;
        };
        log.info("风控检查条件:{},风控值:{},交易值:{},风控检查结果:{}", condition, conditionValue, practicalValue, flag);
        return flag;
    }
}
